home *** CD-ROM | disk | FTP | other *** search
/ PC Media 20 / PC MEDIA CD20.iso / share / prog / cursoasm / cap6.msg < prev    next >
Text File  |  1993-07-05  |  18KB  |  293 lines

  1.                   INTRODUCCION AL ASM: LAS INTERRUPCIONES
  2.                   =======================================
  3.  
  4.    Como comentábamos al final del capítulo dedicado a la pila, en este capítulo
  5. vamos a abordar el tema de las interrupciones. Comencemos pues.
  6.  
  7.    Al principio del curso, vimos el funcionamiento del uP, que contínuamente
  8. recoge instrucciones de la memoria (de la dirección apuntada por CS:IP) y las
  9. ejecuta. Aunque éste es el funcionamiento habitual, hay momentos en los que
  10. esta actividad se tiene que interrumpir. Para ver la razón de la existencia de
  11. las interrupciones, veamos un ejemplo.
  12.    Supongamos que el funcionamiento del teclado fuese el siguiente: el programa
  13. enviaría una señal al teclado, a la que el teclado respondería con un código
  14. indicativo de la tecla. Existiría también un código para indicar 'ninguna
  15. tecla pulsada'. Dentro de este esquema de funcionamiento, imaginemos que un
  16. programa está en un momento dado llevando a cabo una actividad de cálculo, sin
  17. comprobar para nada lo que ocurre con el teclado. Es perfectamente posible que
  18. en ese momento el usuario del ordenador pulse una tecla y la suelte antes de
  19. que el programa compruebe el teclado; si el funcionamiento del teclado fuese el
  20. que hemos visto, esa pulsación de tecla se perdería. Existen varias posibles
  21. soluciones a este problema: una sería que el programa comprobase constantemente
  22. el teclado, con la consiguiente molestia al desarrollar el programa y la pérdi-
  23. da de velocidad. Otra sería añadir hardware al teclado para llevar un buffer
  24. con las teclas pulsadas. Esto ocurre así en los PCs, pero este buffer sólo
  25. tiene capacidad para unas cuatro pulsaciones, con lo que el problema sigue pre-
  26. sentándose. La solución más adecuada, ya que se puede extender a otros proble-
  27. mas parecidos, son las interrupciones hardware, que además es la solución
  28. implementada en los PCs.
  29.  
  30.    Cuando algún periférico necesita que el uP se encargue de algo con urgencia
  31. (por ejemplo, leer el valor de una tecla) pone a nivel activo uno de los pines
  32. del uP, denominado INTR. En este momento, el uP para lo que está haciendo (si
  33. está en medio de la ejecución de una instrucción, se completa ésta primero) y
  34. lee el valor de 8 bits (0 a 255d) de las 8 líneas bajas del bus de datos. El
  35. dispositivo que provoca la interrupción se debe haber encargado de poner en
  36. éstas líneas un código que identifica el número de interrupción. Este número es
  37. un código que identifica el dispositivo que ha provocado la interrupción; por
  38. ejemplo, el teclado tiene el número 9 como identificativo y por tanto pone en
  39. las 8 líneas bajas del bus de datos el valor 00001001b cuando se pulsa una te-
  40. cla. Ahora, lo que ocurre es lo siguiente: el uP no está especialmente diseñado
  41. para manejar algunos dispositivos en concreto, por lo que cuando recibe una in-
  42. terrupción lo que hace es llamar a una rutina que el programador ha dejado re-
  43. sidente en la memoria, que es la que lee la tecla pulsada y la almacena en un
  44. buffer en memoria en el caso de la interrupción 9 (para otros números de inte-
  45. rrupción, se lleva a cabo otra tarea). Esta rutina se suele denominar 'rutina
  46. de servicio a la interrupción', por razones bastante claras (eso espero). La
  47. última instrucción de la rutina de servicio a la interrupción es la instrucción
  48. IRET, que hace que el uP vuelva a la dirección donde estaba antes de la inte-
  49. rrupción. Aún nos queda por ver cómo el uP, a partir del número recibido por el
  50. bus de datos con el número de interrupción, identifica a qué rutina ha de
  51. saltar.
  52.  
  53.    Los diseñadores del 8086 reservaron los primeros 4*256d bytes de la memoria
  54. (1 kilobyte desde la dirección absoluta 00000h hasta la 003FFh, ambas incluí-
  55. das) para una tabla denominada 'tabla de vectores de interrupción'. Esta tabla
  56. contiene cuatro bytes para cada número de interrupción (por eso son 4*256d
  57. bytes, 4 bytes/nº de interrupcion * 256d números de interrupción posibles), que
  58. especifican la dirección de memoria donde comienza la rutina de servicio a cada
  59. interrupción. Al recibir una demanda de interrupción, el micro toma el valor
  60. del bus de datos (el nº de interrupción) y lo multiplica por cuatro. Usa el va-
  61. lor obtenido como dirección de la que leer 4 bytes con la dirección a la que
  62. saltar. Estos cuatro bytes son el offset y el segmento que forman la dirección
  63. absoluta. El ordenamiento Intel especifica que se almacenan primero los bytes
  64. de menor peso, y en el caso del 8086 se almacena primero el offset que el valor
  65. de segmento. Por ejemplo, si instalásemos una rutina de servicio a la interrup-
  66. ción CCh en la dirección de memoria 1122h:3344h, tendríamos que introducir los
  67. siguientes valores en la tabla de vectores de interrupción:
  68.  
  69.            DIRECCION ABSOLUTA        VALOR
  70.            ------------------        -----
  71.            4 * CCh     = 816d         44h
  72.            4 * CCh + 1 = 817d         33h
  73.            4 * CCh + 2 = 818d         22h
  74.            4 * CCh + 3 = 819d         11h
  75.  
  76.    Este es el modelo de funcionamiento, pero todavía no hemos vistos varios
  77. detalles muy importantes. En realidad, la arquitectura del PC hace que los
  78. periféricos no interrumpan directamente al uP (¿qué ocurriría si dos periféri-
  79. cos deciden generar una señal de interrupción a la vez?), sino que éstas están
  80. jerarquizadas mediante unos chips denominados 8259 o PIC ('Programmable
  81. interrupt controller', Controlador de Interrupciones Programable), interactuan-
  82. do de una manera bastante compleja. De momento, esto no nos interesa demasiado.
  83.  
  84.    Cuando el micro recibe la señal de interrupción, el CS y el IP contienen la
  85. dirección de la siguiente instrucción a ejecutar (ya que el IP se incrementa
  86. nada más cargar la instrucción, antes incluso de ejecutarla). Estos se cargan
  87. con los valores leídos de la tabla de vectores de interrupción, por lo que es
  88. necesario preservar su valor actual para luego volver al punto donde se estaba
  89. cuando se produjo la interrupción. Lo que se hace es empujar estos valores en
  90. la pila antes de saltar a la rutina de servicio a la interrupción. Además,
  91. antes de empujar el CS y el IP (en este orden), se empuja el contenido de un
  92. registro que todavía no hemos visto (lo veremos dentro de poco), llamado
  93. 'registro de flags'. Los bits de este registro tienen cada uno un significado
  94. especial, indicando en todo momento el estado actual del uP y, en el caso de
  95. algunos bits, el resultado de la última operación ejecutada (por ejemplo, el
  96. flag ZERO indica si la última operación dio un resultado de 0). Ya que algunos
  97. flags se utilizan para indicar si se esta ejecutando una rutina de servicio a
  98. la interrupción (y por tanto se modifican antes de saltar a la rutina), este
  99. registro se salva a fin de que al volver al hilo normal de ejecución del pro-
  100. grama se restaure completamente el estado anterior de micro.
  101.  
  102.    Ya que una interrupción se puede producir en cualquier momento, ésta debe
  103. ser transparente al programa en ejecución. Cuando se escribe un programa, no
  104. se piensa en que entre una instrucción y la siguiente pueden ejecutarse 200
  105. instrucciones que leen un valor de teclado y lo almacenan en un buffer en
  106. memoria. Por tanto, al volver de una interrupción se tiene que restaurar por
  107. completo el estado del uP, de forma que el programa interrumpido no se vea
  108. afectado. Por ejemplo, supongamos un fragmento de código así:
  109.  
  110.         MOV AX,[200h]
  111.         ADD AX,30h
  112.  
  113.    Si en medio de las dos instrucciones se ejecuta una rutina de servicio a la
  114. interrupción que modifica el contenido de AX, el resultado para el programa
  115. puede ser catastrófico. El uP se encarga de que el registro de flags sea res-
  116. taurado, ya que siempre es modificado. Pero es la propia rutina de servicio a
  117. la interrupción la que debe preservar el contenido de los registros que ésta
  118. modifique. Por tanto, las rutinas de servicio a la interrupción suelen tener el
  119. siguiente aspecto:
  120.  
  121.         PUSH AX
  122.         PUSH BX
  123.         PUSH CX
  124.         PUSH DX
  125.         PUSH DS
  126.         PUSH ES
  127.         PUSH BP
  128.         PUSH SI
  129.         PUSH DI
  130.  
  131.         [...]        ;código que modifica los anteriores 9 registros
  132.  
  133.         POP  DI
  134.         POP  SI
  135.         POP  BP
  136.         POP  ES
  137.         POP  DS
  138.         POP  DX
  139.         POP  CX
  140.         POP  BX
  141.         POP  AX
  142.         IRET        ;retorna al programa interrumpido
  143.  
  144.    Otro de los aspectos importantes de las interrupciones es el de la pila.
  145. Hemos visto que el uP empuja el contenido de los flags, el de CS y el de IP en
  146. la pila apuntada por SS:SP en el momento de la interrupción. Aunque la rutina
  147. de servicio a la interrupción podría modificar SS y SP para usar su propia
  148. pila, esto no es lo habitual; es decir, las rutinas de servicio a la interrup-
  149. ción suelen utilizar la pila del programa interrumpido. Por tanto, aunque vimos
  150. que un POP no borra el valor apuntado por SS:SP después de leerlo, no se puede
  151. suponer que después de un POP los valores inmediatamente por debajo de SS:SP
  152. continúen igual, ya que pueden haber sido pisados debido a una interrupción.
  153.  
  154.    En realidad, cuando se pone a nivel activo la patilla INTR, el uP no siempre
  155. interrumpe el programa en ejecución, sino que primero consulta uno de los bits
  156. del registro de flags, llamado IF ('Interruption Flag', Flag de Interrupción)
  157. y sólo si este está a 1 se atiende la interrupción. En caso de que IF está a 0,
  158. simplemente se ignora la petición de interrupción. Es el 8259 (el PIC) el que
  159. se encarga de volver a generar la interrupción repetidas veces hasta que el uP
  160. está listo. Cuando se salta a una rutina de servicio a la interrupción, una de
  161. las cosas que se hace es poner este bit a 1 de manera que no se pueda interrum-
  162. pir la propia rutina de servicio a la interrupción. Pero esta característica la
  163. puede aprovechar cualquier programa, ya que existen dos instrucciones que ponen
  164. a 0 o a 1 el flag IF:
  165.  
  166.         STI    Pone a 1 el bit IF, para que se atienda a las
  167.             peticiones de interrupción.
  168.         CLI    Pone a 0 el bit IF, para desactivar las interrupciones.
  169.  
  170.    Debido a que se puede controlar el uP para que atienda o no a las peticiones
  171. de servicio a la interrupción, este tipo de interrupciones se denominan
  172. 'Interrupciones enmascarables'; existe otra patilla denominada 'NMI' ('Non
  173. Maskable Interrupt', Interrupción No Enmascarable) de función parecida a INTR,
  174. pero a cuya petición el uP siempre responde. En realidad, no sé con certeza
  175. cómo funcionan las NMI, ya que no se usan habitualmente para la programación.
  176. En el Spectrum, se utilizaba esta interrupción para los 'transfer', aparatos
  177. que permitian en cualquier momento generar una NMI, por medio de un pulsador, y
  178. que se grabara a cinta o disco los registros del uP y los contenidos de toda la
  179. memoria (con cualquier programa que ésta tuviera), para luego poder cargarlo y
  180. continuar la ejecución en el mismo punto; estos eran copiadores infalibles.
  181.  
  182.    Todo esto que hemos visto hasta ahora es interesante para nosotros, pero en
  183. realidad es al diseñador de hardware y al programador de drivers de dispositi-
  184. vos a quien más le concierne. Además, las interrupciones hardware son aún más
  185. complicadas, ya que hay que controlar el PIC, etc... En realidad, sólo en algu-
  186. nos pocos casos nos interesará todo esto (el caso más habitual es el de querer
  187. instalar una rutina de servicio a la interrupción de teclado para poder detec-
  188. tar la pulsación simultánea de varias teclas, como en los juegos). Estas inte-
  189. rrupciones que hemos visto se denominan interrupciones hardware, pero lo que
  190. principalmente nos interesa son las interrupciones software.
  191.  
  192.    Existe una instrucción en ASM llamada INT, que fuerza al uP a realizar el
  193. mismo proceso que lleva a cabo para atender una interrupción. El número de
  194. interrupción no se lee del bus de datos, sino que viene dado por el operando de
  195. la instrucción INT. Por ejemplo, puede usarse la instrucción 'INT 9' para lla-
  196. mar a la rutina de servicio a la interrupción de teclado. En realidad, éste no
  197. es el uso habitual, ya que, por ejemplo, la rutina de servicio a la interrup-
  198. ción 9 espera que el teclado tenga una pulsación de tecla que enviar, por lo
  199. que el funcionamiento de ésta sería anómalo al invocar la INT 9 sin ninguna te-
  200. cla esperando. En realidad, la utilidad de las interrupciones software es otra.
  201.  
  202.    Una parte muy importante de un sistema operativo (quizá la más importante)
  203. es el 'kernel' o núcleo. Este kernel no es más que un conjunto de rutinas o
  204. funciones que ejecutan diversas tareas, como por ejemplo escribir una cadena en
  205. el monitor o escribir datos en un fichero. Estas funciones pueden estar en dis-
  206. tintas direcciones de memoria en distintos ordenadores, por lo que es necesaria
  207. una manera de encontrarlas. En el caso del MS-DOS, la mayoría de los servicios
  208. del kernel están integrados en una función que lleva a cabo distintas acciones
  209. según el contenido del registro AH a la entrada; a cada tarea distinta que se
  210. puede ejecutar en función de AH se le denomina 'servicio'. En los demás regis-
  211. tros se pasan parámetros, cuyo significado varía de un servicio a otro; en caso
  212. de que sea necesario devolver algún valor, se devuelve en los registros. Por
  213. ejemplo, el servicio 9 (AH = 9) imprime una cadena en la pantalla. La cadena se
  214. recoge de la dirección dada por el par DS:DX, y el final de ésta se indica con
  215. el carácter '$'. La función que ofrece todos estos servicios se instala en
  216. memoria cuando se carga el sistema operativo de los ficheros IO.SYS y
  217. MSDOS.SYS. Pero cada vez que se carga puede hacerse en una dirección distinta,
  218. por lo que se almacena en el vector número 21h de la tabla de vectores de inte-
  219. rrupción la dirección donde reside la rutina. Por lo tanto, cuando un programa
  220. quiere invocar algún servicio lo que se hace es ejecutar una instrucción 'INT
  221. 21h'. A modo de ejemplo, veamos qué haríamos para imprimir una cadena almacena-
  222. da en la dirección 1234h:ABCDh :
  223.  
  224.         PUSH DS        ; preserva el contenido de DS
  225.         MOV  AX,1234h    ; segmento de la dir. de la cadena
  226.         MOV  DS,AX    ; en DS
  227.         MOV  DX,ABCDh    ; offset de la dir. de la cadena
  228.         MOV  AH,9    ; servicio 9: escribir cadena
  229.         INT  21h    ; invoca la función del DOS
  230.         POP  DS        ; restaura DS
  231.  
  232.    La interrupción 21h nunca se invoca a causa de una interrupción hardware,
  233. por lo que no se ejecuta entre dos instrucciones cualquiera. Cuando ésta se
  234. ejecuta, es debido a que el programa la ha invocado expresamente. Por tanto, no
  235. es necesario preservar completamente el estado del uP, y de hecho no se hace.
  236. Muchos de los servicios de la interrupción 21h devuelven valores en los regis-
  237. tros, por lo que necesariamente son modificados; en los servicios de ficheros
  238. un flag se utiliza para indicar si ha habido un error, y en caso de que lo haya
  239. habido AX contiene un código de error. SS y SP siempre se conservan (por el
  240. propio funcionamiento de las interrupciones, si la rutina modificase estos re-
  241. gistros no se podría volver al programa), pero los demás registros no siempre
  242. lo hacen. En los manuales de referencia de las interrupciones del DOS suele
  243. venir especificado qué registros se conservan, pero lo habitual es empujar los
  244. registros cuyo contenido interesa conservar en lugar de consultar la referen-
  245. cia. A no ser que se use para devolver algún valor, se suele suponer que DS se
  246. conserva. Si interesa conservar el contenido de algún otro registro, es mejor
  247. empujarlo en la pila.
  248.  
  249.    El BIOS también es, principalmente, un conjunto de funciones que el progra-
  250. mador puede usar. Las funciones son de más bajo nivel que las del DOS, y de
  251. hecho las funciones del DOS suelen valerse de llamadas a la BIOS para cumplir
  252. su tarea. La BIOS puede variar de una placa a otra, pero siempre debe propor-
  253. cionar algunas funciones que vienen definidas por el estándar PC, como por
  254. ejemplo la función para escribir un carácter en la pantalla o la función para
  255. poner un pixel a un color determinado, usada en modo gráfico. Las funciones
  256. gráficas del BIOS están agrupadas en la INT 10h, siendo AH el que especifica el
  257. servicio o función en concreto, y, en algunas funciones, AL la subfunción.
  258.  
  259.    Con lo que ya sabemos, podemos utilizar las interrupciones del BIOS y del
  260. DOS sin más problema. Al igual que para programar en un lenguaje como Pascal o
  261. C es tan importante conocer la sintáxis como las funciones que nos da la
  262. librería estándar de nuestro compilador, para programar en ASM también es nece-
  263. sario conocer las interrupciones del BIOS y el DOS. Que yo sepa, nadie se
  264. aprende los servicios de memoria, por lo que lo habitual es programar con un
  265. manual de referencia de las interrupciones. Por poco dinero, encontraréis en
  266. cualquier librería un libro con estos datos. Se hace casi imprescindible para
  267. programar en ASM, por lo que os recomiendo que os hagáis con uno. Además, a
  268. partir de ahora escribiremos programitas en ASM que habrá que ensamblar y
  269. linkar, por lo que os hará falta el Macro Assembler de Microsoft (abreviadamen-
  270. te MASM) o el Turbo Assembler de Borland (TASM). No os voy a recomendar ninguno
  271. personalmente, ya que las características principales de ambos son iguales,
  272. pero debería transmitiros la opinión generalizada del área de programación ASM
  273. de FidoNET, que se decanta claramente por el TASM. De todas maneras, en el
  274. curso no usaremos características particulares de ninguno de los dos, por lo
  275. que podréis usar cualquiera de los dos para seguirlo.
  276.  
  277.    En el capítulo que viene veremos el 'Hello, world' en ASM, con el objetivo
  278. principal de ver el esqueleto de un listado ASM para el MASM o el TASM. Segui-
  279. remos después con el juego de instrucciones, viendo los flags y los saltos
  280. primero. Cuando conozcamos la mayoría de las instrucciones, comenzaremos a ver
  281. las tareas principales de cualquier programa: interacción con el usuario,
  282. manejo de ficheros, gestión de memoria, etc... Si alguien tiene interés en al-
  283. gún tema en especial, no tiene más que comentármelo. Personalmente, me gustaría
  284. ver algo de programación gráfica, que es mi hobby preferido, pero sólo si hay
  285. gente interesada. Dentro de unos pocos capítulos, iremos viendo a la vez
  286. nuevas instrucciones y servicios que proporcionan el DOS y el BIOS, ya que esto
  287. es un curso y no una referencia de ASM. Al igual que hasta ahora, no busco dar
  288. una definición teórica del ASM (esto no es la Universidad), sino que quiero que
  289. quien siga el curso pueda hacer en ASM todo lo que hace en otros lenguajes.
  290.  
  291.    Salut :-)
  292.  
  293.    Jon